/* WEBPACK VAR INJECTION */(function(global) {
/**
* @module connection
* @author: Hjava
* @description: 连接管理器
* @since: 2018-07-16 16:30:50
*/
Object.defineProperty(exports, "__esModule", { value: true });
var http_1 = __webpack_require__(/*! _core/http */ "./src/core/http/index.ts");
var loginUrlConfig_1 = __webpack_require__(/*! _login/loginUrlConfig */ "./src/core/login/loginUrlConfig.ts");
var analytics_1 = __webpack_require__(/*! _util/analytics */ "./src/core/utils/analytics.ts");
var constant_1 = __webpack_require__(/*! _util/constant */ "./src/core/utils/constant.ts");
var eventEmitter_1 = __webpack_require__(/*! _util/eventEmitter */ "./src/core/utils/eventEmitter.ts");
var owlController_1 = __webpack_require__(/*! _util/owlController */ "./src/core/utils/owlController.ts");
var userData_1 = __webpack_require__(/*! _util/userData */ "./src/core/utils/userData.ts");
var isIE_1 = __webpack_require__(/*! _util/isIE */ "./src/core/utils/isIE.ts");
var log_1 = __webpack_require__(/*! ./../utils/log */ "./src/core/utils/log.ts");
var lvs_report_1 = __webpack_require__(/*! ../statistics/lvs-report */ "./src/core/statistics/lvs-report.ts");
var login_report_1 = __webpack_require__(/*! ../statistics/login-report */ "./src/core/statistics/login-report.ts");
var deviceInfo_1 = __webpack_require__(/*! ../utils/deviceInfo */ "./src/core/utils/deviceInfo.ts");
var MWebSocket;
if (false) {}
else {
    MWebSocket = __webpack_require__(/*! _login/websocket */ "./src/core/login/websocket.ts").default;
}
var LongPolling;
if (false) {}
else {
    LongPolling = __webpack_require__(/*! _login/longPolling */ "./src/core/login/longPolling.ts").default;
}
// WebSocket 连接时长 P99
var WEBSOCKET_TIMEOUT = 15 * 1000;
var ONLINE_WEBSOCKET_TIMEOUT = 4 * 1000;
// 使用最长登录时间统计值
var LONG_POLING_TIMEOUT = 15 * 1000;
var RECONNECT_TIME = 2 * 1000;
var WEBSOCKET_REG = /wss?:\/\/[^:]+(:\d+)?/;
// 最小从后端获取长连接/长轮询地址时间
var MIN_GET_REMOTE_URL_INTERVAL = 10 * 1000;
// WebSocket 失败重试次数
var WEBSOCKET_RETRY_MAX_NUMBER = 3;
var CONNECTION_TIMEOUT_ANALYTICS_TIME = 15 * 1000;
var Connection = /** @class */ (function () {
    function Connection(_a) {
        var _this = this;
        var _b = _a.autoConnect, autoConnect = _b === void 0 ? true : _b;
        this._autoReconnectMark = false;
        this._connection = null;
        this._onlyUseLongPolling = false; // 是否只用长轮询连接
        this._status = constant_1.LOGIN_STATUS.DISCONNECTED;
        this._singleConnectTimeoutHandle = 0;
        this._reconnectTimeoutHandle = 0;
        this._connectNetworkOnlineStatus = true;
        this._currentUrl = '';
        this._websocketUrls = [];
        this._longPollingUrls = [];
        this._connectTimeoutTimer = 0;
        this._useLongPolling = false; // 是否支持连接降级到长轮询。默认关闭，因为目前所有业务的浏览器都支持websocket了。长轮询连接时间长，服务压力大体验不好
        this._lastDetectTime = -1;
        this._lastGetRemoteUrlTime = 0;
        this._currentWebsocketRetryNumber = 0;
        this._daemon = null;
        this.connectionType = constant_1.CONNECT_TYPE.WEBSOCKET;
        this._onlyUseLongPolling = userData_1.default.get('loginWay') === 'bosh';
        this._useLongPolling = userData_1.default.get('useLongPolling') === true;
        this._autoReconnectMark = autoConnect;
        log_1.default.log('connection::constructor _onlyUseLongPolling:' + this._onlyUseLongPolling + ' _useLongPolling:' + this._useLongPolling + ' _autoReconnectMark:' + this._autoReconnectMark);
        this._initLoginUrl();
        if (this._autoReconnectMark) {
            this._autoReconnect();
        }
        if (false) {}
        else if (false) {}
        else {
            if (global) {
                global.addEventListener('offline', function () {
                    log_1.default.log('connection::constructor global offline _status:' + _this._status);
                    deviceInfo_1.default.setHasNetwork(false);
                    if (_this._status !== constant_1.LOGIN_STATUS.DISCONNECTED) {
                        _this.disconnect('offline');
                    }
                    clearTimeout(_this._connectTimeoutTimer);
                    _this._connectTimeoutTimer = 0;
                });
                global.addEventListener('online', function () {
                    // 如果网络恢复，优先使用 WebSocket
                    log_1.default.log('connection::constructor global online _status:' + _this._status);
                    deviceInfo_1.default.setHasNetwork(true);
                    if (_this._autoReconnectMark) {
                        _this.reconnectWhenConnectionDisconnected();
                    }
                });
            }
        }
        // 休眠检测打码，保证在休眠唤醒重新执行代码时能够被检测出来。假如定时器触发的时候时间已经超过30秒则认为机器休眠了
        setInterval(function () {
            var now = Date.now();
            if (_this._lastDetectTime > 0 && now - _this._lastDetectTime > 30 * 1000) {
                log_1.default.log('connection::constructor sleep setInterval run');
                _this.quickPing();
            }
            _this._lastDetectTime = now;
        }, 5 * 1000);
        if (false) {}
    }
    Connection.prototype.connectWhenInit = function (shouldConnect) {
        if (shouldConnect === void 0) { shouldConnect = true; }
        log_1.default.log('connection::connectWhenInit shouldConnect:' + shouldConnect);
        if (shouldConnect) {
            this.reconnect();
        }
    };
    Connection.prototype.connect = function () {
        var _this = this;
        this._initConnectAnalytics();
        var url = this._getLoginUrl();
        if (url === this._currentUrl && this.connectionType === constant_1.CONNECT_TYPE.WEBSOCKET && this._connectNetworkOnlineStatus) {
            this._currentWebsocketRetryNumber++;
        }
        else {
            this._currentWebsocketRetryNumber = 1;
        }
        this._connectNetworkOnlineStatus = this.getNetWorkStatus();
        this._connection.connect(url);
        this._connection.url = url;
        this._currentUrl = url;
        this._connection.onOpen(this.onConnectSuccess.bind(this));
        this._connection.onMessage(this.onMessage.bind(this));
        this._connection.onClose(this.onClose.bind(this));
        this._connection.onError(this.onError.bind(this));
        log_1.default.log('connection::connect url:' + url + ' id:' + this._connection.sessionId);
        login_report_1.default.reportSocketStart({ id: this._connection.sessionId, connectType: this.connectionType });
        this._changeConnectStatus(constant_1.LOGIN_STATUS.CONNECTING, 'connecting');
        this._singleConnectTimeoutHandle = setTimeout(function () {
            log_1.default.log('connection::connect timeout url:' + url + ' id:' + _this._connection.sessionId);
            login_report_1.default.reportSocketTimeout({ id: _this._connection.sessionId });
            _this._useNextConnection();
        }, this.connectionType === constant_1.CONNECT_TYPE.WEBSOCKET ? (userData_1.default.get('env') === constant_1.ENV.ONLINE ? ONLINE_WEBSOCKET_TIMEOUT : ONLINE_WEBSOCKET_TIMEOUT) : LONG_POLING_TIMEOUT);
        var networkStatus = this.getNetWorkStatus();
        analytics_1.default.report({
            nm: 'login_connect',
            val: {
                net: networkStatus ? 1 : 0
            }
        });
        if (networkStatus) {
            owlController_1.default.send({
                name: 'login_connect',
                value: 0,
                tag: {
                    status: 0
                }
            });
        }
    };
    /**
     * 需要处理长连接假在线情况
     * 注：可能会断开一条正在登陆的连接——后建立的连接先登录， 由于时序原因断开的连接后登陆成功再收到关闭帧，会让前面的正常使用的连接收到 KickOffWithSameDeviceId 包
     *
     * @memberof Connection
     */
    Connection.prototype.reconnect = function () {
        if (this._status !== constant_1.LOGIN_STATUS.AUTHENTICATING && this._status !== constant_1.LOGIN_STATUS.CONNECTING) {
            this._useNextConnection();
        }
    };
    Connection.prototype.reconnectWhenConnectionDisconnected = function () {
        if (this._status === constant_1.LOGIN_STATUS.DISCONNECTED) {
            this._useNextConnection();
        }
    };
    Connection.prototype.getNetWorkStatus = function () {
        if (false) {}
        if (!global || !global.navigator) {
            return true;
        }
        return global.navigator.onLine;
    };
    Connection.prototype._autoReconnect = function () {
        var _this = this;
        eventEmitter_1.default.on(constant_1.LOGIN_STATUS_CHANGE_EVENT, function () {
            if (_this._autoReconnectMark) {
                switch (_this._status) {
                    case constant_1.LOGIN_STATUS.DISCONNECTED: {
                        log_1.default.log('connection::_autoReconnect LOGIN_STATUS.DISCONNECTED');
                        clearTimeout(_this._reconnectTimeoutHandle);
                        _this._reconnectTimeoutHandle = setTimeout(function () {
                            log_1.default.log('connection::_autoReconnect run');
                            _this.reconnectWhenConnectionDisconnected();
                        }, RECONNECT_TIME);
                        break;
                    }
                }
            }
        });
    };
    Connection.prototype.setAutoConnectToFalse = function () {
        this._autoReconnectMark = false;
    };
    Connection.prototype.disconnect = function (reason) {
        if (reason === void 0) { reason = ''; }
        log_1.default.log('connection::disconnect reason:' + reason);
        this._changeConnectStatus(constant_1.LOGIN_STATUS.DISCONNECTING, reason);
        this._connection.disconnect();
        this._changeConnectStatus(constant_1.LOGIN_STATUS.DISCONNECTED, reason);
    };
    Connection.prototype.send = function (data, needLogin) {
        if (needLogin === void 0) { needLogin = true; }
        if (!this._connection) {
            return;
        }
        if (this._status !== constant_1.LOGIN_STATUS.AUTHENTICATED) {
            log_1.default.log('connection::send' + ' id:' + this._connection.sessionId + ' status:' + this._status + ' needLogin:' + needLogin);
        }
        if (this._status === constant_1.LOGIN_STATUS.AUTHENTICATED || !needLogin) {
            this._connection.send(data.getBytes());
        }
    };
    Connection.prototype.onConnectSuccess = function () {
        log_1.default.log('connection::onConnectSuccess' + ' id:' + this._connection.sessionId);
        login_report_1.default.reportSocketSuccess({ id: this._connection.sessionId });
        this._changeConnectStatus(constant_1.LOGIN_STATUS.CONNECTED, 'connected');
        this._resetTimer();
        this._connectedReset();
        var _startConnectTime = userData_1.default.get('_startConnectTime');
        analytics_1.default.report({
            nm: 'login_connect_success',
            val: {
                socket_time: _startConnectTime ? Date.now() - _startConnectTime : void 0
            }
        });
        owlController_1.default.send({
            name: 'login_connect_finished',
            value: _startConnectTime ? Date.now() - _startConnectTime : 0,
            tag: {
                status: 1,
                isWebSocket: this.connectionType === constant_1.CONNECT_TYPE.WEBSOCKET ? 1 : 0
            }
        });
        userData_1.default.remove('_startConnectTime');
        clearTimeout(this._connectTimeoutTimer);
        this._connectTimeoutTimer = 0;
    };
    Connection.prototype.onMessage = function (data) {
        eventEmitter_1.default.emit('_dataRecv', data);
    };
    Connection.prototype.onClose = function (sessionId) {
        if (sessionId === void 0) { sessionId = ''; }
        log_1.default.log('connection::onClose' + ' id:' + this._connection.sessionId + ' closeId:' + sessionId);
        if (this._connection.sessionId !== sessionId) {
            return;
        }
        login_report_1.default.reportSocketFail({ id: this._connection.sessionId });
        this._resetTimer();
        // 触发关闭时，有可能是客户端主动关闭，也有可能是服务端主动或被动关闭
        if (this._status !== constant_1.LOGIN_STATUS.CONNECTING || !this._connectNetworkOnlineStatus || !this.getNetWorkStatus() || this._currentWebsocketRetryNumber < WEBSOCKET_RETRY_MAX_NUMBER) {
            this._connectedReset();
        }
        this.disconnect('onClose');
        if (this.getNetWorkStatus()) {
            owlController_1.default.send({
                name: 'login_connect_finished',
                value: 0,
                tag: {
                    status: 0
                }
            });
        }
    };
    Connection.prototype.onError = function () {
        // 当连接异常时，会触发onClose
        // this.disconnect('onError');
    };
    Connection.prototype.isWebSocket = function () {
        return this.connectionType === constant_1.CONNECT_TYPE.WEBSOCKET;
    };
    Connection.prototype.isLongPolling = function () {
        return this.connectionType === constant_1.CONNECT_TYPE.LONG_POLLING;
    };
    Connection.prototype.getStatus = function () {
        return this._status;
    };
    Connection.prototype._getLoginUrl = function () {
        // 可能会出现用shouldUsePolling判断时数据异步请求获取回来的情况，所以缓存用connectionType来进行判断
        if (this.connectionType === constant_1.CONNECT_TYPE.WEBSOCKET) {
            if (this._websocketUrls.length === 0) {
                this._putLocalWebSocketUrlInArray();
            }
            return this._websocketUrls.shift() || '';
        }
        else {
            return this._longPollingUrls[0];
        }
    };
    Connection.prototype._getRemoteWebSocketUrl = function () {
        var _this = this;
        analytics_1.default.report('login_lvs_start');
        var now = Date.now();
        if (now - this._lastGetRemoteUrlTime < MIN_GET_REMOTE_URL_INTERVAL) {
            this._putLocalWebSocketUrlInArray();
            this._putLocalLongPollingUrlInArray();
            analytics_1.default.report({
                nm: 'login_lvs_local_success'
            });
            log_1.default.log('connection::_getRemoteWebSocketUrl MIN_GET_REMOTE_URL_INTERVAL');
            return;
        }
        log_1.default.log('connection::_getRemoteWebSocketUrl start');
        this._lastGetRemoteUrlTime = now;
        http_1.default.getRemoteLoginAddr({
            success: function (_a) {
                var _b;
                var _c = _a.websocketAddr, websocketAddr = _c === void 0 ? [] : _c, _d = _a.longPollingAddr, longPollingAddr = _d === void 0 ? [] : _d;
                log_1.default.log('connection::_getRemoteWebSocketUrl success websocketAddr:' + websocketAddr + ' longPollingAddr:' + longPollingAddr);
                if (websocketAddr.length > 0) {
                    websocketAddr = websocketAddr.map(function (element) {
                        if (userData_1.default.get('useHTTPS') || userData_1.default.get('env') === constant_1.ENV.ONLINE) {
                            return "wss://" + element;
                        }
                        else {
                            return "ws://" + element;
                        }
                    });
                    var websocketUrls = websocketAddr.filter(function (url) {
                        // wss协议必须符合wss协议规范，即"wss://yf.neixin.cn:8600"这种格式
                        return WEBSOCKET_REG.test(url);
                    });
                    if (websocketUrls.length > 0) {
                        (_b = _this._websocketUrls).push.apply(_b, websocketUrls);
                        _this._websocketUrls = _this._removeRepeatUrls(_this._websocketUrls);
                    }
                    else {
                        _this._putLocalWebSocketUrlInArray();
                    }
                }
                if (longPollingAddr.length > 0) {
                    _this._longPollingUrls = longPollingAddr.map(function (element) {
                        if (userData_1.default.get('useHTTPS') || userData_1.default.get('env') === constant_1.ENV.ONLINE) {
                            return "https://" + element;
                        }
                        else {
                            return "http://" + element;
                        }
                    });
                }
                else {
                    _this._putLocalLongPollingUrlInArray();
                }
                analytics_1.default.report({
                    nm: 'login_lvs_success',
                    val: {
                        host: websocketAddr.join(',')
                    }
                });
                lvs_report_1.default.reportLVS({ resultCode: 0, time: Date.now() - now, domainUrl: websocketAddr.join(',') });
            },
            error: function (error) {
                log_1.default.log('connection::_getRemoteWebSocketUrl fail');
                if (_this._websocketUrls.length === 0) {
                    _this._putLocalWebSocketUrlInArray();
                }
                // 不存在这种场景，不过使用此方案来兼容异常情况
                if (_this._longPollingUrls.length === 0) {
                    _this._putLocalLongPollingUrlInArray();
                }
                analytics_1.default.report('login_lvs_error');
                lvs_report_1.default.reportLVS({ resultCode: -1, time: Date.now() - now, errorMsg: error + '' });
            }
        });
    };
    Connection.prototype._putLocalWebSocketUrlInArray = function () {
        var _a;
        (_a = this._websocketUrls).push.apply(_a, this._getConfigLoginUrl(constant_1.CONNECT_TYPE.WEBSOCKET));
        this._websocketUrls = this._removeRepeatUrls(this._websocketUrls);
    };
    Connection.prototype._putLocalLongPollingUrlInArray = function () {
        this._longPollingUrls = this._getConfigLoginUrl(constant_1.CONNECT_TYPE.LONG_POLLING);
    };
    Connection.prototype._useNextConnection = function () {
        // 一定需要保障每次只能建立一根websocket连接
        this._resetTimer();
        if (this._connection) {
            this._connection.disconnect();
        }
        if (!this._shouldUseLongPolling()) {
            log_1.default.log('connection::_useNextConnection !this._shouldUseLongPolling()');
            this._initWebSocket();
            this.connect();
        }
        else if (!this._useLongPolling) {
            // 如果不降级到长轮询，使用默认值连接后从远端获取地址
            log_1.default.log('connection::_useNextConnection !this._useLongPolling');
            this._currentWebsocketRetryNumber = 0;
            this._putLocalWebSocketUrlInArray();
            this._useNextConnection();
            this._getRemoteWebSocketUrl();
        }
        else {
            log_1.default.log('connection::_useNextConnection _degradeConnection');
            this._degradeConnection();
        }
    };
    Connection.prototype._degradeConnection = function () {
        this._currentWebsocketRetryNumber = 0;
        this._getRemoteWebSocketUrl();
        if (this.connectionType === constant_1.CONNECT_TYPE.WEBSOCKET || this._onlyUseLongPolling) {
            this._initLongPolling();
            this.connect();
        }
        else {
            //todo 会触发断线重连，需要处理
            this._changeConnectStatus(constant_1.LOGIN_STATUS.DISCONNECTED, 'disconnected');
        }
    };
    Connection.prototype._initWebSocket = function () {
        log_1.default.log('connection::_initWebSocket');
        this._connection = new MWebSocket();
        this.connectionType = constant_1.CONNECT_TYPE.WEBSOCKET;
    };
    Connection.prototype._initLongPolling = function () {
        log_1.default.log('connection::_initLongPolling');
        this._connection = new LongPolling();
        this.connectionType = constant_1.CONNECT_TYPE.LONG_POLLING;
    };
    Connection.prototype._initLoginUrl = function () {
        this._putLocalWebSocketUrlInArray();
        this._putLocalLongPollingUrlInArray();
    };
    Connection.prototype._getConfigLoginUrl = function (type) {
        if (type === void 0) { type = constant_1.CONNECT_TYPE.WEBSOCKET; }
        return loginUrlConfig_1.default.getConfig(userData_1.default.get('env'), userData_1.default.get('useHTTPS'), userData_1.default.get('appId'), type, userData_1.default.get('useChatRoomURL'));
    };
    Connection.prototype._changeConnectStatus = function (status, reason) {
        log_1.default.log('connection::_changeConnectStatus status:' + status + ' reason:' + reason);
        this._status = status;
        eventEmitter_1.default.emit(constant_1.LOGIN_STATUS_CHANGE_EVENT, status, reason);
    };
    Connection.prototype._resetTimer = function () {
        clearTimeout(this._reconnectTimeoutHandle);
        this._connectNetworkOnlineStatus = true;
        clearTimeout(this._singleConnectTimeoutHandle);
        this._singleConnectTimeoutHandle = null;
    };
    Connection.prototype._connectedReset = function () {
        if (this.connectionType === constant_1.CONNECT_TYPE.WEBSOCKET) {
            this._websocketUrls.unshift(this._connection.url);
            this._websocketUrls = this._removeRepeatUrls(this._websocketUrls);
        }
    };
    /**
     * should use long polling or not, when the brower is IE, set  'true' of _onlyUseLongPolling value or websocket urls is empty
     *
     * @private
     * @returns boolean
     * @memberof Connection
     */
    Connection.prototype._shouldUseLongPolling = function () {
        return this._onlyUseLongPolling || ((isIE_1.default || this._websocketUrls.length === 0) && this._currentWebsocketRetryNumber >= WEBSOCKET_RETRY_MAX_NUMBER);
    };
    Connection.prototype._initConnectAnalytics = function () {
        // 有网络并且是第一次有网尝试建立连接
        if (!userData_1.default.get('_startConnectTime') && !this._connectTimeoutTimer || !this.getNetWorkStatus()) {
            userData_1.default.set('_startConnectTime', Date.now());
            userData_1.default.set('_startConnectToLoginTime', Date.now());
            clearTimeout(this._connectTimeoutTimer);
            this._connectTimeoutTimer = setTimeout(function () {
                analytics_1.default.report({
                    nm: 'login_connect_timeout',
                    val: {
                        passport: userData_1.default.get('passport'),
                        uid: userData_1.default.get('uid')
                    }
                });
            }, CONNECTION_TIMEOUT_ANALYTICS_TIME);
        }
    };
    Connection.prototype._removeRepeatUrls = function (addr) {
        var ret = new Array();
        for (var i = 0; i < addr.length; i++) {
            var item = addr[i];
            var bFind = false;
            for (var j = 0; j < ret.length; j++) {
                var retItem2 = ret[j];
                if (retItem2 === item) {
                    bFind = true;
                    break;
                }
            }
            if (!bFind) {
                ret.push(item);
            }
        }
        return ret;
    };
    Connection.prototype.setDaemon = function (daemon) {
        this._daemon = daemon;
    };
    Connection.prototype.quickPing = function () {
        if (this._daemon) {
            this._daemon.quickPing();
        }
    };
    return Connection;
}());
exports.default = Connection;

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

//# sourceURL=webpack://MTDX/./src/core/login/connection.ts?